package com.tarena.test.elasticsearch;

import com.alibaba.fastjson2.JSON;
import com.tarena.test.elasticsearch.builder.IndexBuilder;
import com.tarena.test.elasticsearch.pojo.Student;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.collapse.CollapseBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.Before;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class ESSearchTestRun {
    private RestHighLevelClient client;
    @Before
    public void initClient(){
        HttpHost httpHost=new HttpHost("localhost",9200,"http");
        RestClientBuilder builder= RestClient.builder(httpHost);
        client=new RestHighLevelClient(builder);
    }
    //构建一部分测试数据
    @Test
    public void buildData() throws Exception{
        IndexBuilder.buildData("students",50);
    }
    //一个es的搜索 包含几部分 查询条件 分页 排序 聚合
    //返回结果页包含几部分 查询命中 元数据(分片 大小 版本等)
    //案例1 我们想要搜索所有的文档
    @Test
    public void searchAll() throws Exception{
        //1. 构造一个查询条件
        SearchRequest request=new SearchRequest("students");
        SearchSourceBuilder sourceBuilder=new SearchSourceBuilder();
        //2. 具体的查询条件 匹配所有
        MatchAllQueryBuilder queryBuilder=new MatchAllQueryBuilder();
        //3. 封装查询数据
        sourceBuilder.query(queryBuilder);
        //4. 封装分页
        sourceBuilder.from(0);
        sourceBuilder.size(10);
        //5. 排序 比如使用生日倒序
        sourceBuilder.sort("birthday", SortOrder.DESC);
        request.source(sourceBuilder);
        //6. 请求查询
        SearchResponse search = client.search(request, RequestOptions.DEFAULT);
        //7. 解析查询结果
        //7.1 查询命中总数 分页相关
        System.out.println("查询命中总数:"+search.getHits().getTotalHits().value);
        //7.2 查询数据返回
        SearchHit[] hits = search.getHits().getHits();
        List<Student> students =new ArrayList<>();
        for (SearchHit hit : hits) {
            //到这里hit里包含的就是doc数据 我们先打印一下
            hit.getSourceAsMap().forEach((k,v)->{
                System.out.println(k+":"+v);
            });
            Student student = JSON.parseObject(JSON.toJSONString(hit.getSourceAsMap()), Student.class);
            students.add(student);
        }
        System.out.println(students);
    }
    //不同的搜索逻辑,我们只需要改一下查询条件即可
    //案例2 termQuery 查询 利用text文本类型的分词查询 这个查询只有匹配到和匹配不到的问题
    @Test
    public void searchByTerm() throws Exception{
        SearchRequest request=new SearchRequest("students");
        SearchSourceBuilder sourceBuilder=new SearchSourceBuilder();
        TermQueryBuilder queryBuilder=QueryBuilders.termQuery("name","学");
        sourceBuilder.query(queryBuilder);
        request.source(sourceBuilder);
        SearchResponse search = client.search(request, RequestOptions.DEFAULT);
        System.out.println("查询命中总数:"+search.getHits().getTotalHits().value);
        SearchHit[] hits = search.getHits().getHits();
        for (SearchHit hit : hits) {
            hit.getSourceAsMap().forEach((k,v)->{
                System.out.println(k+":"+v);
            });
            System.out.println("该学生评分:"+hit.getScore());
        }
    }
    //案例3 matchquery 这个会根据匹配评分
    @Test
    public void searchByMatch() throws Exception{
        SearchRequest request=new SearchRequest("students");
        SearchSourceBuilder sourceBuilder=new SearchSourceBuilder();
        //如果一个学生名称是 张春生 另一个是 王生生 matchquery会把王生生的评分调高
        MatchQueryBuilder queryBuilder=QueryBuilders.matchQuery("name","生");
        sourceBuilder.query(queryBuilder);
        request.source(sourceBuilder);
        SearchResponse search = client.search(request, RequestOptions.DEFAULT);
        System.out.println("查询命中总数:"+search.getHits().getTotalHits().value);
        SearchHit[] hits = search.getHits().getHits();
        for (SearchHit hit : hits) {
            hit.getSourceAsMap().forEach((k,v)->{
                System.out.println(k+":"+v);
            });
            System.out.println("该学生评分:"+hit.getScore());
        }
    }
    //案例4 布尔查询,我们在查询一些数据的时候,条件并不唯一,所以可以使用布尔查询匹配文档
    @Test
    public void searchByQueryAndFilter() throws Exception{
        SearchRequest searchRequest = new SearchRequest("students");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder booleanQueryBuilder = QueryBuilders.boolQuery();
        //比如我要查询1.名字是张生 2.生日大于2019年1月1日
        booleanQueryBuilder.must(QueryBuilders.matchQuery("name","生"));
        booleanQueryBuilder.filter(QueryBuilders.rangeQuery("birthday").gt("2000-01-01"));
        //booleanQueryBuilder.must(QueryBuilders.rangeQuery("birthday").gt("2000-01-01"));
        searchSourceBuilder.query(booleanQueryBuilder);
        searchRequest.source(searchSourceBuilder);
        SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
        System.out.println("查询命中总数:"+search.getHits().getTotalHits().value);
        SearchHit[] hits = search.getHits().getHits();
        for (SearchHit hit : hits) {
            hit.getSourceAsMap().forEach((k,v)->{
                System.out.println(k+":"+v);
            });
            System.out.println("该学生评分:"+hit.getScore());
        }
    }
    //案例5 根据某个字段去重 比如这里的birthday
    @Test
    public void searchByCollapse() throws Exception{
        SearchRequest searchRequest = new SearchRequest("students");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder booleanQueryBuilder = QueryBuilders.boolQuery();
        //比如我要查询1.名字是张生 2.生日大于2019年1月1日
        booleanQueryBuilder.must(QueryBuilders.matchQuery("name","学生"));
        //booleanQueryBuilder.filter(QueryBuilders.rangeQuery("birthday").gt("2000-01-01"));
        //booleanQueryBuilder.must(QueryBuilders.rangeQuery("birthday").gt("2000-01-01"));
        CollapseBuilder collapseBuilder = new CollapseBuilder("gender");
        searchSourceBuilder.query(booleanQueryBuilder).collapse(collapseBuilder);
        searchRequest.source(searchSourceBuilder);
        SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
        System.out.println("查询命中总数:"+search.getHits().getTotalHits().value);
        SearchHit[] hits = search.getHits().getHits();
        for (SearchHit hit : hits) {
            hit.getSourceAsMap().forEach((k,v)->{
                System.out.println(k+":"+v);
            });
            System.out.println("该学生评分:"+hit.getScore());
        }
    }
    //查询N公里内的坐标数据
    @Test
    public void searchByGeoDistance() throws Exception{
        SearchRequest searchRequest = new SearchRequest("students");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //给定一个坐标,这里当前中心为坐标 从微信小程序中可以看到
        GeoPoint geoPoint = new GeoPoint(39.9,116.4);
        GeoDistanceQueryBuilder geoQueryBuilder =
                QueryBuilders.geoDistanceQuery("location")
                        .point(geoPoint).distance(50D, DistanceUnit.KILOMETERS);
        //构造布尔
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(geoQueryBuilder);
        boolQueryBuilder.must(QueryBuilders.matchQuery("name","黄"));
        //还想查询名称叫做黄昭昭,或者性黄的
        //分页
        searchSourceBuilder.from(3);
        searchSourceBuilder.size(3);
        searchSourceBuilder.query(boolQueryBuilder);
        searchRequest.source(searchSourceBuilder);
        SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);

        long total = search.getHits().getTotalHits().value;
        //打印命中总数
        System.out.println("查询命中总数:"+total);
        for (SearchHit searchHit : search.getHits().getHits()) {
            Map<String, Object> sourceAsMap = searchHit.getSourceAsMap();
            System.out.println(sourceAsMap);
            System.out.println("==========================");
        }
    }
}
